import sys
import os
import re
from jinja2 import Template
import subprocess
from typing import List, Dict
import json
import copy

from .Util import Util
from .DDSUtil import DDSUtil
from .AtomServiceUtil import AtomServiceUtil
from ..entity import Client, Message
from ..config import (
    cpp_proto_dict,
    cpp_idl_dict,
)


class ClientUtil:

    async_client_suffix = "_async_client"
    sync_client_suffix = "_sync_client"
    dds_client_suffix = "_dds_node"

    def __init__(self, client):
        pass

    @staticmethod
    def accomplishAllSyncClient(orchestrattion_info: dict):
        """
        完成所有客户端的所有流程
        """
        # 确保文件夹存在，并将json保存到文件夹

        client: Client = ClientUtil.generateSyncClient(orchestrattion_info)
        client_path: str = (
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/"
        )
        # 将orchestrattion_info保存到文件夹
        with open(client_path + f"{client.get_name()}.json", "w") as f:
            json.dump(orchestrattion_info, f, indent=4, ensure_ascii=False)
        # 生成通信模块代码
        Util.generateHeaderHppAndCpp(client_path + "sync_client/")

        ClientUtil.generateSyncClientCMakeLists(client)
        ClientUtil.compileSyncClient(client)
        ClientUtil.generateExecShell(client._name)

    @staticmethod
    def accomplishAllDDSClient(orchestrattion_info: dict):
        """
        完成所有客户端的所有流程
        """
        # 确保文件夹存在，并将json保存到文件夹

        client: Client = ClientUtil.generateDDSClient(orchestrattion_info)
        client_path: str = (
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/"
        )
        # 将orchestrattion_info保存到文件夹
        with open(client_path + f"{client.get_name()}.json", "w") as f:
            json.dump(orchestrattion_info, f, indent=4, ensure_ascii=False)
        ClientUtil.generateDDSClientCMakeLists(client)
        ClientUtil.compileDDSClient(client)
        ClientUtil.generateExecShell(client._name)

    @staticmethod
    def generateSyncClientCMakeLists(client: Client):
        client_template = Template(
            open(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/sync_server_client_process_template/sync_client_compile_cmake_template.j2"
            ).read()
        )

        res_str = client_template.render(
            project_name=client.get_name() + ClientUtil.sync_client_suffix,
            services=client._services,
            idl_list=client._dds_module,
        )

        # 确保目录存在
        os.makedirs(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/sync_client/",
            exist_ok=True,
        )

        # 将res_str写入框架内的cpp文件中，同名不同路径
        with open(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/sync_client/CMakeLists.txt",
            "w",
        ) as file:
            file.write(res_str)
        print(
            f"SUCCESS: {os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/sync_client/CMakeLists.txt generated!"
        )

    @staticmethod
    def generateDDSClientCMakeLists(client: Client):
        client_template = Template(
            open(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/dds_client_process_template/dds_node_compile_cmake_template_withSE.j2"
            ).read()
        )

        res_str = client_template.render(
            services=client._services,
            idl_list=client._dds_module,
        )

        # 确保目录存在
        os.makedirs(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/dds_client/",
            exist_ok=True,
        )

        # 将res_str写入框架内的cpp文件中，同名不同路径
        with open(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/dds_client/CMakeLists.txt",
            "w",
        ) as file:
            file.write(res_str)
        print(
            f"SUCCESS: {os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/dds_client/CMakeLists.txt generated!"
        )

    @staticmethod
    def compileSyncClient(client: Client):
        client_dir = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/sync_client/"

        Util.compileCmakeProject(
            client_dir,
            client.get_name() + ClientUtil.sync_client_suffix,
            file_type="cpp",
        )

    @staticmethod
    def compileDDSClient(client: Client):
        client_dir = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client.get_name()}/dds_client/"

        Util.compileCmakeProject(
            client_dir,
            "multiple",
            file_type="cpp",
        )

    @staticmethod
    def generateSyncClient(orchestrattion_info: dict) -> Client:
        """
        client头部模版生成
        目前假设：
        1. 开始节点转发的idl只有基本类型而且只有一个结构体
        2. dds数据结构在开始node的dataBind的idl中
        """
        # TODO: 通过json生成client
        # 获取出现的service列表（出现多次只记录一次）
        # 定义idl的目录 idl_path + idl名称（module）
        # idl_path: str = (
        #     f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{orchestrattion_info.get('name', '')}/idl"
        # )
        # 解析并编译开始和结束节点中的结构体为idl文件
        DDSUtil.compileStructs(orchestrattion_info)
        # 头文件需要引入的idl名称列表
        idl_list: List[str] = list()

        # 定义输出字符串
        output_str: str = ""

        # 获取使用到的service的名称列表（不重复）
        service_list: List[str] = list()
        for node in orchestrattion_info.get("nodes", []):
            if (
                node.get("type", "") == "SERVICE"
                and node.get("name", "") not in service_list
            ):
                service_list.append(node.get("name", ""))
        # 为orchestrattion_info的node添加服务的具体信息，即添加一个service_info属性
        for node in orchestrattion_info.get("nodes", []):
            if node.get("type", "") == "SERVICE":
                node["service_info"] = AtomServiceUtil.getAtomServiceJson(
                    node.get("name", "")
                )
            if node.get("type", "") == "START" or node.get("type", "") == "END":
                node["message_info"] = ClientUtil._getIdlJson(
                    orchestrattion_info.get("name", ""), node.get("module", "")
                )

        # 加载 “开始” 中的输出数据
        #  “开始” 节点列表
        start_node_list: list[dict] = list()
        for node in orchestrattion_info.get("nodes", []):
            if node.get("type", "") == "START":
                start_node_list.append(node)
                # 将module名称加入idl_list
                idl_list.append(node.get("module", ""))

        # end节点的列表
        end_node_list: List[dict] = list()
        for node in orchestrattion_info.get("nodes", []):
            if node.get("type", "") == "END":
                end_node_list.append(node)
                # 将module名称加入idl_list
                idl_list.append(node.get("module", ""))

        # 保存指向end节点的node
        end_prev_node_list: list[dict] = list()
        for connection in orchestrattion_info.get("connections", []):
            if connection.get("target", "") in [
                end_node.get("id", "") for end_node in end_node_list
            ]:
                end_prev_node_list.append(
                    ClientUtil._getNode(
                        orchestrattion_info, connection.get("source", "")
                    )
                )

        # 定义声明对象的列表List[message_id, node_id]
        declare_service_request_list: List[list] = list()
        for node in orchestrattion_info.get("nodes", []):
            if node.get("type", "") == "SERVICE":
                temp_list: list = list()
                temp_list.append(node.get("name", ""))
                temp_list.append(
                    ClientUtil._getRequestMessage(node, node.get("method", "")).get(
                        "name", ""
                    )
                )
                temp_list.append(node.get("id", ""))
                declare_service_request_list.append(temp_list)

        # “开始” 节点的连接列表,Dict[id, List[dict]]
        StartNodeId_connection_dict: Dict[str, List[dict]] = dict()
        for start_node in start_node_list:
            start_connection_list: list[dict] = list()
            for connection in orchestrattion_info.get("connections", []):
                if connection.get("source", "") == start_node.get("id", ""):
                    start_connection_list.append(connection)
            StartNodeId_connection_dict[start_node.get("id", "")] = (
                start_connection_list
            )

        # 定义赋值关系
        start_assignment_list: List[list] = list()

        for start_node in start_node_list:
            # # 遍历连接列表，获取连接的目标节点的id
            connection_list: list[dict] = StartNodeId_connection_dict.get(
                start_node.get("id", ""), []
            )
            for connection in connection_list:
                target_node_id = connection.get("target", "")
                # 获取目标节点的信息
                target_node = ClientUtil._getNode(orchestrattion_info, target_node_id)
                start_assignment_list.extend(
                    ClientUtil._getAssignmentResult(
                        source_id=start_node.get("id", ""),
                        source_node=start_node,
                        target_id=target_node_id,
                        target_node=target_node,
                    )
                )

        # 加载client_header模版
        client_header_template = Template(
            open(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/sync_server_client_process_template/sync_client_header_template.j2"
            ).read()
        )
        client_header_str = client_header_template.render(
            orchestrattion_info=orchestrattion_info,
            service_list=service_list,
            idl_list=idl_list,
            start_node_list=start_node_list,
            end_node_list=end_node_list,
            start_assignment_list=start_assignment_list,
            declare_service_request_list=declare_service_request_list,
        )

        # 将 client_header 的字符串添加到 output_str
        output_str = output_str + client_header_str

        # 运行阶段
        """
        client中部模版生成
        
        
        如果一个节点已经运行添加字段used = true
        为每个节点添加一个prev_id_list属性，标记前置节点数量
        为每个节点添加一个assign_num属性，标记当前节点的赋值数量,只有等于len(prev_id_list)时，才可以运行
        """
        # 创建上面三个属性并赋初值
        for node in orchestrattion_info.get("nodes", []):
            if node.get("type", "") != "START":
                node["used"] = False
                node["prev_id_list"] = list()
                node["assign_num"] = 0
                for connection in orchestrattion_info.get("connections", []):
                    if connection.get("target", "") == node.get("id", ""):
                        node["prev_id_list"].append(connection.get("source", ""))

        # 找到开始节点后可以运行的节点
        for node in start_node_list:
            for connection in orchestrattion_info.get("connections", []):
                if connection.get("source", "") == node.get("id", ""):
                    # 将该节点的assign_num加1
                    target_node = ClientUtil._getNode(
                        orchestrattion_info, connection.get("target", "")
                    )
                    target_node["assign_num"] += 1

        # 标志位，表明是否结束
        end_flag = False
        while not end_flag:
            # 如果一个节点的运算已经完成，添加一个node属性为uesd = true，否则该属性为空
            # available_list存储当前可以执行的节点, call_template_list存储当前可以执行的节点的模版需要字段的列表
            available_list: List[dict] = list()
            call_template_list: List[str] = list()
            for node in orchestrattion_info.get("nodes", []):
                if (
                    node.get("type", "") != "START" and node.get("type", "") != "END"
                    and node["assign_num"] == len(node["prev_id_list"])
                    and node["used"] == False
                ):
                    available_list.append(node)
                    node["used"] = True
                    temp_call_template: list = list()
                    temp_call_template.append(node.get("name", ""))
                    temp_call_template.append(
                        ClientUtil._getResponseMessage(node, node["method"]).get(
                            "name", ""
                        )
                    )
                    temp_call_template.append(node.get("id", ""))
                    temp_call_template.append(node.get("name", ""))
                    temp_call_template.append(node.get("method", ""))
                    temp_call_template.append(node.get("id", ""))
                    call_template_list.append(temp_call_template)

            # 定义赋值关系
            content_assignment_list: List[list] = list()

            # 遍历所有available节点，将available节点的输出数据传给下一级节点
            for prev_node in available_list:
                # 找到以prev_node 开始的所有连线
                connection_list: list[dict] = list()
                for connection in orchestrattion_info.get("connections", []):
                    if connection.get("source", "") == prev_node.get("id", ""):
                        # 将该节点的assign_num(赋值次数)加1
                        ClientUtil._getNode(
                            orchestrattion_info, connection.get("target", "")
                        )["assign_num"] += 1
                        # 如果下一级节点是end节点，不需要添加
                        if connection.get("target", "") in [
                            end_node.get("id", "") for end_node in end_node_list
                        ]:
                            continue
                        connection_list.append(connection)
                # 遍历所有连线，将连线的目标节点的输入数据赋值给下一级节点, 只考虑一层嵌套
                for connection in connection_list:
                    target_node_id = connection.get("target", "")
                    # 获取目标节点的信息
                    target_node = ClientUtil._getNode(
                        orchestrattion_info, target_node_id
                    )
                    content_assignment_list.extend(
                        ClientUtil._getAssignmentResult(
                            source_id=prev_node.get("id", ""),
                            source_node=prev_node,
                            target_id=target_node_id,
                            target_node=target_node,
                        )
                    )

            # 写入输出字符串
            client_content_template = Template(
                open(
                    f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/sync_server_client_process_template/sync_client_content_template.j2"
                ).read()
            )
            client_content_str = client_content_template.render(
                call_template_list=call_template_list,
                content_assignment_list=content_assignment_list,
            )
            output_str = output_str + client_content_str
            # 判断循环是否应该结束,所有end节点的 assign_num都大于等于len(prev_id_list)，结束循环
            if all(
                node["assign_num"] >= len(node["prev_id_list"])
                for node in end_node_list
            ):
                end_flag = True

        """
        client尾部模版生成
        
        
        构造client的尾部字符串
        """
        # “开始” 节点的连接列表,Dict[id, List[str]]
        endPrevNodeId_connection_dict: Dict[str, List[dict]] = dict()
        for end_prev_node in end_prev_node_list:
            end_connection_list: list[dict] = list()
            for connection in orchestrattion_info.get("connections", []):
                if connection.get("source", "") == end_prev_node.get("id", "") and connection.get("target", "") in [end_node.get("id", "") for end_node in end_node_list]:
                    end_connection_list.append(connection)
            endPrevNodeId_connection_dict[end_prev_node.get("id", "")] = (
                end_connection_list
            )
        # 定义赋值关系
        end_assignment_list: List[list] = list()
        for end_prev_node in end_prev_node_list:
            # 遍历连接列表，获取连接的目标节点的id
            connection_list: list[dict] = endPrevNodeId_connection_dict.get(
                end_prev_node.get("id", ""), []
            )
            for connection in connection_list:
                target_node_id = connection.get("target", "")
                # 获取目标节点的信息
                target_node = ClientUtil._getNode(orchestrattion_info, target_node_id)
                end_assignment_list.extend(
                    ClientUtil._getAssignmentResult(
                        source_id=end_prev_node.get("id", ""),
                        source_node=end_prev_node,
                        target_id=target_node_id,
                        target_node=target_node,
                    )
                )

        # 加载client_footer模版
        client_footer_template = Template(
            open(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/sync_server_client_process_template/sync_client_footer_template.j2"
            ).read()
        )
        client_footer_str = client_footer_template.render(
            orchestrattion_info=orchestrattion_info,
            service_list=service_list,
            start_node_list=start_node_list,
            end_assignment_list=end_assignment_list,
            end_node_list=end_node_list,
        )
        output_str = output_str + client_footer_str

        """
        保存文件到client目录
        """
        os.makedirs(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{orchestrattion_info.get('name', '')}/sync_client/",
            exist_ok=True,
        )
        client_file = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{orchestrattion_info.get('name', '')}/sync_client/{orchestrattion_info.get('name', '') + ClientUtil.sync_client_suffix}.cpp"
        with open(client_file, "w", encoding="utf-8") as f:
            f.write(output_str)
        # 输出字符串
        print("SUCCESS: generated " + client_file)

        """
        构建并返回一个client对象
        """
        res_client = Client()
        res_client.set_name(orchestrattion_info.get("name", ""))
        # print("client: " + res_client.get_name())
        # 这里将module的内容作为topic导入，未来需要将client中的topic改为module
        for start_node in start_node_list:
            res_client.add_dds_module(start_node.get("module", ""))
        for end_node in end_node_list:
            res_client.add_dds_module(end_node.get("module", ""))
        for service_name in service_list:
            res_client.add_service(service_name)
        return res_client

    @staticmethod
    def generateDDSClient(orchestrattion_info: dict) -> Client:
        """
        本函数为生成DDS服务编排代码的函数。
        输入参数：
        orchestrattion_info: dict, 服务编排的前端输入json，没有开始和结束节点

        输出参数：
        client: Client, 生成的client对象
        在输出client对象之前，已经将代码生成完毕。
        """
        # 获取使用到的service的名称列表（不重复）
        service_name_list: List[str] = list()
        # 获取使用到的service的数据列表（名称重复，id不重复。因此会出现同一服务多次出现的情况，但他们的id都不同）
        # service_info_list: List[dict] = list()
        # node的id和节点之间的映射关系
        node_id_service_dict: Dict[int, dict] = dict()

        for node in orchestrattion_info.get("nodes", []):
            if (
                node.get("type", "") == "SERVICE"
                and node.get("name", "") not in service_name_list
            ):
                service_name_list.append(node.get("name", ""))

        # 为orchestrattion_info的node添加服务的具体信息，即添加一个service_info属性
        for node in orchestrattion_info.get("nodes", []):
            node["service_info"] = AtomServiceUtil.getAtomServiceJson(
                    node.get("name", "")
                )
            node_id_service_dict[node.get("id", "")] = node

        """
        对于每个service节点，找到它的上一个节点和下一个节点的列表

        service_input_output_dict[service_id]["input"]代表输入节点id列表
        service_input_output_dict[service_id]["output"]代表输出节点id列表
        """
        service_input_output_dict: Dict[str, Dict[str, List[str]]] = dict()
        for connection in orchestrattion_info.get("connections", []):
            source_id = connection.get("source", "")
            target_id = connection.get("target", "")
            if source_id in node_id_service_dict:
                if source_id not in service_input_output_dict:
                    service_input_output_dict[source_id] = dict()
                if "output" not in service_input_output_dict[source_id]:
                    service_input_output_dict[source_id]["output"] = list()
                service_input_output_dict[source_id]["output"].append(target_id)
            if target_id in node_id_service_dict:
                if target_id not in service_input_output_dict:
                    service_input_output_dict[target_id] = dict()
                if "input" not in service_input_output_dict[target_id]:
                    service_input_output_dict[target_id]["input"] = list()
                service_input_output_dict[target_id]["input"].append(source_id)

        """
        整理模版数据，并写入文件
        """
        for service_id, service_dict in service_input_output_dict.items():
            # 在节点中的service节点信息
            service_info: dict = node_id_service_dict.get(service_id)
            service_input_struct: dict = ClientUtil._getRequestMessage(
                service_info, service_info.get("method", "")
            )
            service_output_struct: dict = ClientUtil._getResponseMessage(
                service_info, service_info.get("method", "")
            )
            input_idl_module_path_list: List[str] = list()  # 输入的idl模块路径列表
            output_idl_module_path_list: List[str] = list()  # 输出的idl模块路径列表
            # 全局数据结构的元素列表 global_data_list
            # 第 0 个元素是node_id
            # 第 1 个元素是服务名称
            # 第 2 个元素是方法名称
            # 第 3 个元素是结构体名称
            global_data_list: List[List[str]] = list()
            # 输入值与当前服务输入结构体的赋值关系
            input_assignment_list_dict: Dict[str, List[List[str]]] = dict()
            # 获取输入输出的服务信息
            for input_id in service_dict.get("input", []):
                node: dict = node_id_service_dict.get(input_id)
                if node.get("type") == "SERVICE":
                    input_idl_module_path_list.append(
                        f"../../../atomic_service/{node.get('name', '')}/idl/{node.get('name', '')}.h"
                    )
                    # 获取输入值的相关数据
                    global_data: List[str] = [
                        input_id,
                        node.get("name", ""),
                        node.get("method", ""),
                        ClientUtil._getResponseMessage(
                            node, node.get("method", "")
                        ).get("name", ""),
                        "",
                    ]
                    global_data_list.append(global_data)
                # 获取输入值与当前服务输入结构体的赋值关系
                input_assignment_list_dict[input_id] = ClientUtil._getAssignmentResult(
                    source_id=node.get("id", ""),
                    source_node=node,
                    target_id=service_id,
                    target_node=service_info,
                    data_type="idl",
                )
            for output_id in service_dict.get("output", []):
                node: dict = node_id_service_dict.get(output_id)
                if node.get("type") == "SERVICE":
                    output_idl_module_path_list.append(
                        f"../../../atomic_service/{node.get('name', '')}/idl/{node.get('name', '')}.h"
                    )

            # 获取服务返回值的结构体的变量列表
            output_struct_var_list: List[List[str]] = ClientUtil._getVariableResult(
                service_info, service_output_struct.get("name", "")
            )

            """
            加载并写入模版
            """
            # 加载模版
            dds_client_template = Template(
                open(
                    f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/dds_client_process_template/dds_node_template.j2"
                ).read()
            )
            dds_client_str = dds_client_template.render(
                orchestrattion_name=orchestrattion_info.get("name", ""),
                node_id_service_dict=node_id_service_dict,
                input_idl_module_path_list=input_idl_module_path_list,
                output_idl_module_path_list=output_idl_module_path_list,
                global_data_list=global_data_list,
                service_info=service_info,
                service_input_struct=service_input_struct,
                service_output_struct=service_output_struct,
                input_assignment_list_dict=input_assignment_list_dict,
                output_struct_var_list=output_struct_var_list,
            )
            # 写入模版
            os.makedirs(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{orchestrattion_info.get('name', '')}/dds_client/",
                exist_ok=True,
            )
            client_file = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{orchestrattion_info.get('name', '')}/dds_client/{service_info.get('name', '') +ClientUtil.dds_client_suffix}.cpp"
            with open(client_file, "w", encoding="utf-8") as f:
                f.write(dds_client_str)
            # 输出字符串
            print("SUCCESS: generated " + client_file)

        """
        构建并返回一个client对象
        """
        res_client = Client()
        res_client.set_name(orchestrattion_info.get("name", ""))
        for service_name in service_name_list:
            res_client.add_service(service_name)
        return res_client

    @staticmethod
    def generateDDSClient_withSE(orchestrattion_info: dict) -> Client:
        """
        本函数为生成DDS服务编排代码的函数。

        本函数的输入参数与grpc版本的服务编排完全一致，包含开始和结束节点。
        开始和结束节点的内容包括与传感器DDS程序连接的接口信息。
        开始节点的内容包括与传感器DDS程序连接的接口信息，结束节点的内容包括与执行器DDS程序连接的接口信息。

        输入的dict内容详见doc/orchestrate_json_format/grpc/fxkz_processed.json
        """

        """
        解析并编译开始和结束节点中的结构体为idl文件
        """
        DDSUtil.compileStructs(orchestrattion_info)
        # 头文件需要引入的idl名称列表
        idl_name_list: List[str] = list()
        # 获取需要引入的idl数据列表
        # idl_info_list: List[dict] = list()

        # 获取使用到的service的名称列表（不重复）,获取service的dict列表（可以重复）
        service_name_list: List[str] = list()
        # 获取使用到的service的数据列表（名称重复，内容不重复，会出现同一服务多次出现的情况）
        service_info_list: List[dict] = list()
        # service的id和服务之间的映射关系
        service_id_service_dict: Dict[str, dict] = dict()
        # node的id和节点之间的映射关系
        node_id_service_dict: Dict[str, dict] = dict()
        for node in orchestrattion_info.get("nodes", []):
            if (
                node.get("type", "") == "SERVICE"
                and node.get("name", "") not in service_name_list
            ):
                service_name_list.append(node.get("name", ""))
        # 为orchestrattion_info的node添加服务的具体信息，即添加一个service_info属性
        for node in orchestrattion_info.get("nodes", []):
            node_id_service_dict[node.get("id", "")] = node
            if node.get("type", "") == "SERVICE":
                node["service_info"] = AtomServiceUtil.getAtomServiceJson(
                    node.get("name", "")
                )
                service_info_list.append(node)
                service_id_service_dict[node.get("id", "")] = node
            if node.get("type", "") == "START" or node.get("type", "") == "END":
                node["message_info"] = ClientUtil._getIdlJson(
                    orchestrattion_info.get("name", ""), node.get("module", "")
                )

        # 加载 “开始” 中的输出数据
        #  “开始” 节点列表
        start_node_list: list[dict] = list()
        for node in orchestrattion_info.get("nodes", []):
            if node.get("type", "") == "START":
                start_node_list.append(node)
                # 将module名称加入idl_list
                idl_name_list.append(node.get("module", ""))

        # end节点的列表
        end_node_list: List[dict] = list()
        for node in orchestrattion_info.get("nodes", []):
            if node.get("type", "") == "END":
                end_node_list.append(node)
                # 将module名称加入idl_list
                idl_name_list.append(node.get("module", ""))

        """
        对于每个service节点，找到它的上一个节点和下一个节点的列表

        service_input_output_dict[service_id]["input"]代表输入节点id列表
        service_input_output_dict[service_id]["output"]代表输出节点id列表
        """
        service_input_output_dict: Dict[str, Dict[str, List[str]]] = dict()
        for connection in orchestrattion_info.get("connections", []):
            source_id = connection.get("source", "")
            target_id = connection.get("target", "")
            if source_id in node_id_service_dict:
                if source_id not in service_input_output_dict:
                    service_input_output_dict[source_id] = dict()
                if "output" not in service_input_output_dict[source_id]:
                    service_input_output_dict[source_id]["output"] = list()
                service_input_output_dict[source_id]["output"].append(target_id)
            if target_id in node_id_service_dict:
                if target_id not in service_input_output_dict:
                    service_input_output_dict[target_id] = dict()
                if "input" not in service_input_output_dict[target_id]:
                    service_input_output_dict[target_id]["input"] = list()
                service_input_output_dict[target_id]["input"].append(source_id)
        # 删除不是service节点的
        for service_id in list(service_input_output_dict.keys()):
            if service_id not in service_id_service_dict:
                del service_input_output_dict[service_id]
        """
        整理模版数据，并写入文件
        """
        for service_id, service_dict in service_input_output_dict.items():
            service_info: dict = node_id_service_dict.get(service_id)
            service_input_struct: dict = ClientUtil._getRequestMessage(
                service_info, service_info.get("method", "")
            )
            service_output_struct: dict = ClientUtil._getResponseMessage(
                service_info, service_info.get("method", "")
            )
            input_idl_module_path_list: List[str] = list()  # 输入的idl模块路径列表
            output_idl_module_path_list: List[str] = list()  # 输出的idl模块路径列表
            # 全局数据结构的元素列表
            # 第 0 个元素是node_id
            # 第 1 个元素是服务名称，即idl_module名称
            # 第 2 个元素是方法名称，如果是start或者end节点，此为空字符串
            # 第 3 个元素是结构体名称
            # 第 4 个元素是start或者end节点的topic名称。如果是service节点，此为空字符串。
            global_data_list: List[List[str]] = list()
            input_assignment_list: List[list] = (
                list()
            )  # 输入值与当前服务输入结构体的赋值关系
            output_assignment_list: List[list] = (
                list()
            )  # 输出值与当前服务输入结构体的赋值关系
            # 如果需要返回结束节点，则添加一个变量用来储存结束节点信息
            end_node_assignment_list: List[List[List[str]]] = list()
            # 获取输入输出的服务信息
            for input_id in service_dict.get("input", []):
                node: dict = node_id_service_dict.get(input_id)
                if node.get("type") == "SERVICE":
                    input_idl_module_path_list.append(
                        f"../../../atomic_service/{node.get('name', '')}/idl/{node.get('name', '')}.h"
                    )
                    # 获取输入值的相关数据
                    global_data: List[str] = [
                        input_id,
                        node.get("name", ""),
                        node.get("method", ""),
                        ClientUtil._getResponseMessage(
                            node, node.get("method", "")
                        ).get("name", ""),
                        "",
                    ]
                    global_data_list.append(global_data)
                else:
                    input_idl_module_path_list.append(
                        f"../idl/{node.get('module', '')}/{node.get('module', '')}.h"
                    )
                    # 获取输入值的相关数据
                    global_data: List[str] = [
                        input_id,
                        node.get("module", ""),
                        "start_or_end_node",
                        node.get("struct_name", ""),
                        node.get("topic", ""),
                    ]
                    global_data_list.append(global_data)
                # 获取输入值与当前服务输入结构体的赋值关系
                input_assignment_list.append(
                    ClientUtil._getAssignmentResult(
                        source_id=node.get("id", ""),
                        source_node=node,
                        target_id=service_id,
                        target_node=service_info,
                        data_type="idl",
                    )
                )
            for output_id in service_dict.get("output", []):
                node: dict = node_id_service_dict.get(output_id)
                if node.get("type") == "SERVICE":
                    output_idl_module_path_list.append(
                        f"../../../atomic_service/{node.get('name', '')}/idl/{node.get('name', '')}.h"
                    )
                else:
                    output_idl_module_path_list.append(
                        f"../idl/{node.get('module', '')}/{node.get('module', '')}.h"
                    )
                    end_node_assignment_list.append(
                        ClientUtil._getAssignmentResult(
                            source_id=service_id,
                            source_node=service_info,
                            target_id=node.get("id", ""),
                            target_node=node,
                            data_type="idl",
                        )
                    )
                # 获取输出值与当前服务输入结构体的赋值关系
                output_assignment_list.append(
                    ClientUtil._getAssignmentResult(
                        source_id=service_id,
                        source_node=service_info,
                        target_id=node.get("id", ""),
                        target_node=node,
                        data_type="idl",
                    )
                )

            # 获取服务返回值的结构体的变量列表

            output_struct_var_list: List[List[str]] = ClientUtil._getVariableResult(
                service_info, service_output_struct.get("name", "")
            )

            """
            加载并写入模版
            """
            # 加载模版
            dds_client_template = Template(
                open(
                    f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/dds_client_process_template/dds_node_template_withSE.j2"
                ).read()
            )
            dds_client_str = dds_client_template.render(
                orchestrattion_name=orchestrattion_info.get("name", ""),
                node_id_service_dict=node_id_service_dict,
                input_idl_module_path_list=input_idl_module_path_list,
                output_idl_module_path_list=output_idl_module_path_list,
                global_data_list=global_data_list,
                input_assignment_list=input_assignment_list,
                output_assignment_list=output_assignment_list,
                end_node_assignment_list=end_node_assignment_list,
                service_info=service_info,
                service_input_struct=service_input_struct,
                service_output_struct=service_output_struct,
                output_struct_var_list=output_struct_var_list,
            )
            # 写入模版
            os.makedirs(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{orchestrattion_info.get('name', '')}/dds_client/",
                exist_ok=True,
            )
            client_file = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{orchestrattion_info.get('name', '')}/dds_client/{service_info.get('name', '') +ClientUtil.dds_client_suffix}.cpp"
            with open(client_file, "w", encoding="utf-8") as f:
                f.write(dds_client_str)
            # 输出字符串
            print("SUCCESS: generated " + client_file)

        """
        构建并返回一个client对象
        """
        res_client = Client()
        res_client.set_name(orchestrattion_info.get("name", ""))
        # print("client: " + res_client.get_name())
        # 这里将module的内容作为topic导入，未来需要将client中的topic改为module
        for start_node in start_node_list:
            res_client.add_dds_module(start_node.get("module", ""))
        for end_node in end_node_list:
            res_client.add_dds_module(end_node.get("module", ""))
        for service_name in service_name_list:
            res_client.add_service(service_name)
        return res_client

    @staticmethod
    def _getServiceName(orchestrattion_info: dict, id: str) -> str:
        for node in orchestrattion_info.get("nodes", []):
            if node.get("id", "") == id:
                return node.get("name", "")

    @staticmethod
    def _getNode(orchestrattion_info: dict, id: str) -> dict:
        for node in orchestrattion_info.get("nodes", []):
            if node.get("id", "") == id:
                return node

    # 根据method_name获取message的dict
    @staticmethod
    def _getMessage(target_node: dict, message_name: str) -> dict:
        if target_node.get("type", "") == "SERVICE":
            for message in target_node.get("service_info", {}).get("messages", []):
                if message.get("name", "") == message_name:
                    return message
        else:
            for message in target_node.get("message_info", {}).get("messages", []):
                if message.get("name", "") == message_name:
                    return message

    # 根据method_name获取request的message
    @staticmethod
    def _getRequestMessage(target_node: dict, method_name: str) -> dict:
        request_message = ""
        for method in target_node.get("service_info", {}).get("methods", []):
            if method.get("name", "") == method_name:
                request_message = method.get("requestMsg", "")
        for message in target_node.get("service_info", {}).get("messages", []):
            if message.get("name", "") == request_message:
                return message
        return dict()

    # 根据method_name获取response的message
    @staticmethod
    def _getResponseMessage(target_node: dict, method_name: str) -> dict:
        response_message = ""
        for method in target_node.get("service_info", {}).get("methods", []):
            if method.get("name", "") == method_name:
                response_message = method.get("responseMsg", "")
        for message in target_node.get("service_info", {}).get("messages", []):
            if message.get("name", "") == response_message:
                return message
        return dict()

    @staticmethod
    def _getStartOrEndNodeMessage(target_node: dict) -> dict:
        """
        在开始或者结束节点中查找并返回message的dict
        查找的条件是：
        1. type为START或END
        2. struct_name和message的name相同
        """
        if (
            target_node.get("type", "") == "START"
            or target_node.get("type", "") == "END"
        ):
            for message in target_node.get("message_info", {}).get("messages", []):
                if message.get("name", "") == target_node.get("struct_name", ""):
                    return message
        return dict()

    # 判断该字段是否是一个结构体，如果是，返回结构体，否则返回空dict
    @staticmethod
    def isStruct(target_node: dict, field: dict) -> dict:
        for message in target_node.get("service_info", {}).get("messages", []):
            if message.get("name", "") == field.get("name", ""):
                return message
        return dict()

    @staticmethod
    def getSourceNodeNum(orchestrattion_info: dict, id: str) -> int:
        num: int = 0
        for connection in orchestrattion_info.get("connections", []):
            if connection.get("target", "") == id:
                num += 1
        return num

    @staticmethod
    def _getIdlJson(client_name: str, struct_name: str) -> dict:
        """
        获取idl的json文件
        """
        # 打开idl的json文件
        idl_file = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/client/{client_name}/idl/{struct_name}/{struct_name}.json"
        with open(idl_file, "r", encoding="utf-8") as f:
            idl_json = json.load(f)
            return idl_json
        return dict()

    @staticmethod
    def _getAssignmentResult(
        source_id: str,
        source_node: dict,
        target_id: str,
        target_node: dict,
        data_type: str = "proto",
    ) -> List[List[str]]:
        """
        通过两个节点获取赋值结果，可以多层嵌套，直到找到所有名称相同的变量

        第一个元素是source的字段的字符串，第二个元素是target的字段的字符串
        第三个元素是source_id，第四个元素是target_id
        第五个是元素的类型，根据data_type来判断是proto还是idl
        """
        # 返回值，一个list的list，
        result: List[List[str]] = []

        # 类型判断
        res_type: str = ""
        if data_type == "proto":
            res_type = "type_proto"
        elif data_type == "idl":
            res_type = "type_idl"

        # 获取source_node的message和target_node的message
        source_message_name: str = ""
        source_message: dict = dict()
        source_messages: list = list()
        target_message_name: str = ""
        target_message: dict = dict()
        target_messages: list = list()
        if (
            source_node.get("type", "") == "START"
            or source_node.get("type", "") == "END"
        ):
            source_message_name = source_node.get("struct_name", "")
            source_message = ClientUtil._getStartOrEndNodeMessage(source_node)
            source_messages = source_node.get("message_info", {}).get("messages", [])
        else:
            source_message = ClientUtil._getResponseMessage(
                source_node, source_node.get("method", "")
            )
            source_message_name = source_message.get("name", "")
            source_messages = source_node.get("service_info", {}).get("messages", [])

        if (
            target_node.get("type", "") == "START"
            or target_node.get("type", "") == "END"
        ):
            target_message_name = target_node.get("struct_name", "")
            target_message = ClientUtil._getStartOrEndNodeMessage(target_node)
            target_messages = target_node.get("message_info", {}).get("messages", [])
        else:
            target_message = ClientUtil._getRequestMessage(
                target_node, target_node.get("method", "")
            )
            target_message_name = target_message.get("name", "")
            target_messages = target_node.get("service_info", {}).get("messages", [])

        """
        一些函数内部函数
        """

        def find_message_by_type(messages, message_type) -> dict:
            # 在messages列表中根据name查找对应的message
            for msg in messages:
                if msg["name"] == message_type:
                    return msg
            return dict()

        def traverse(current_path: list, source_fields: list, target_fields: list):
            # 递归遍历比较字段
            for s_field in source_fields:
                for t_field in target_fields:
                    if (
                        s_field["name"] == t_field["name"]
                        and s_field["type"] == t_field["type"]
                    ):
                        if (
                            find_message_by_type(target_messages, t_field["type"])
                            == dict()
                        ):
                            # 基本类型，添加到结果
                            source_path = copy.deepcopy(current_path)
                            target_path = copy.deepcopy(current_path)
                            source_path.append(s_field["name"])
                            target_path.append(t_field["name"])
                            result.append([source_path, target_path, s_field[res_type]])
                        else:
                            # 消息类型，递归处理
                            s_msg = find_message_by_type(
                                source_messages, s_field["type"]
                            )
                            t_msg = find_message_by_type(
                                target_messages, t_field["type"]
                            )
                            if s_msg and t_msg:
                                new_path = copy.deepcopy(current_path)
                                new_path.append(s_field["name"])
                                traverse(new_path, s_msg["fields"], t_msg["fields"])

        """"
        实际的运行函数
        """
        # 获取source和target的顶层message
        source_top = find_message_by_type(source_messages, source_message_name)
        target_top = find_message_by_type(target_messages, target_message_name)

        if source_top and target_top:
            traverse(list(), source_top["fields"], target_top["fields"])

        # 处理result，添加两个id
        for item in result:
            item[0] = ".".join(item[0])
            item[1] = ".".join(item[1])
            # 将type移动到第五个位置
            field_type = item.pop(2)  # 移除并获取type
            item.append(source_id)
            item.append(target_id)
            item.append(field_type)
        return result

    @staticmethod
    def _getVariableResult(
        node: dict, struct_name: str, suffix: str = ""
    ) -> List[List[str]]:
        """
        获取节点的变量结果列表
        可以多层嵌套，直到找到所有基本类型的变量，组成列表
        @return: List[List[str]]
        返回值是一个列表，列表的每个元素也是一个列表
        其中，
        第一个元素是变量的名称，
        第二个元素是变量的类型
        """
        result: List[List[str]] = []
        message: dict = ClientUtil._getMessage(node, struct_name)
        # 获取node的message_name_list，即message的名称列表
        message_name_list: List[str] = list()
        for message_info in node.get("service_info", {}).get("messages", []):
            message_name_list.append(message_info.get("name", ""))
        for field_info in message.get("fields", []):
            if field_info.get("type", "") in message_name_list:
                # 递归处理
                result.extend(
                    ClientUtil._getVariableResult(
                        node,
                        field_info.get("type", ""),
                        suffix + field_info.get("name", ""),
                    )
                )
            else:
                # 基本类型，添加到结果
                result.append(
                    [suffix + field_info.get("name", ""), field_info.get("type", "")]
                )
        # print(result)
        return result

    @staticmethod
    def generateExecShell(client_name: str):
        shell_template = Template(
            open(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/monitor_deployment_template/exec_client_shell_template.j2"
            ).read()
        )
        res_str = shell_template.render(
            client_name=client_name,
        )
        # 确保目录存在
        os.makedirs(
            "/opt/",
            exist_ok=True,
        )

        # 将res_str写入框架内的cpp文件中，同名不同路径
        with open(
            os.path.join("/opt/", f"{client_name}.sh"),
            "w",
        ) as file:
            file.write(res_str)
        print(f"SUCCESS: generated /opt/{client_name}.sh")